/*
 * ksContainerBlockWriter.cpp
 *
 *  Created on: Aug 3, 2011
 *      Author: wang
 */

#include "ksContainerBlockWriter.h"
#include <cstring>
#include <exception>
#include <fstream>

#include "../ksAtomBlock.h"
#include "../ksDefines.h"
#include "../ksBlockEntry.h"
#include "ksCatalogAtomBlockWriter.h"
#include "../ksException.h"
using std::exception;

/*
 * 向容器中添加一个block
 */
void ksContainerBlockWriter::addBlock(const ksBlock* block)
{
    _blockHeader.container.entryCount++;

    ksBlockEntry* entry = new ksBlockEntry();
    entry->setId(block->id());
    entry->setType(block->type());

    _entries.push_back(entry);

    _blocks.push_back((ksBlock*) block);
}

/*
 * 计算数据长度,完善索引表数据
 *  blockPositionInFile block在文件中的位置
 */
void ksContainerBlockWriter::prepare(int blockPositionInFile)
{
    blockLength();

    std::vector<ksBlockEntry*>::iterator it;
    int offset = 0;
    int blockIndex = 0;
    for (it = _entries.begin(); it != _entries.end(); it++, blockIndex++)
    {
        (*it)->setOffset(offset);
        (*it)->setLength(_blocks[blockIndex]->writer()->blockLength());
        offset += _align4((*it)->length());
    }

    unsigned int blockDataPosition = blockPositionInFile + _blockHeader.container.entryTableSize;
    unsigned int subBlockPostionInFile = blockDataPosition;
    std::vector<ksBlock*>::iterator itBlock;
    for (itBlock = _blocks.begin(); itBlock != _blocks.end(); itBlock++)
    {
        ksBlock* block = *itBlock;
        if (kBlockMainTag == block->type() || kBlockPartTag == block->type() || kBlockFileTag == block->type())
        {
            block->writer()->prepare(subBlockPostionInFile);
        }
        subBlockPostionInFile += block->writer()->blockLength();
    }

    /* 更新catalog block中资源的offset */
    if (type() == kBlockMain || type() == kBlockPart)
    {
        ksCatalogAtomBlockWriter* catalogBlock = NULL;
        std::vector<ksBlock*>::iterator it;
        // search the catalog block
        for (it = _blocks.begin(); it != _blocks.end(); it++)
        {
            ksBlock* block = *it;
            if (block->type() == kBlockCatalog)
            {
                catalogBlock = (ksCatalogAtomBlockWriter*) block;
                break;
            }
        }

        if (!catalogBlock)
        {
            throw ksException("should be only one catalog block in main or part block");
        }

        std::vector<ksBlockEntry*>::iterator entryIt;

        for (entryIt = _entries.begin(); entryIt != _entries.end(); entryIt++)
        {
            catalogBlock->updateEntryOffset(*(*entryIt), blockPositionInFile + _blockHeader.container.entryTableSize);
        }
    }
}

/*
 * 计算block所有数据的长度
 * 计算公式:
 *  blockLength = headerLength + entryTableLength + SUM( subBlockLength )
 *   headerLength = kContainerBlockHeaderLength
 *   entryTableLength = kBlockEntryLength * entryCount
 */
unsigned int ksContainerBlockWriter::blockLength()
{
    if (_blockHeader.container.dataLength == 0)
    {
        std::vector<ksBlock*>::iterator it;
        for (it = _blocks.begin(); it != _blocks.end(); it++)
        {
            _blockHeader.container.dataLength += (*it)->writer()->blockLength();
        }

        _blockHeader.container.entryTableSize = kContainerBlockHeaderLength + kBlockEntryLength * _blockHeader.container.entryCount;
    }
    return _blockHeader.container.dataLength + _blockHeader.container.entryTableSize;
}

/*
 * 保存到输出流
 * 示意图:
 *  block header info
 *  entry table info
 *  sub block data
 *  sub block data
 *  sub block data
 *  ...
 */
void ksContainerBlockWriter::save(std::ofstream & out)
{
    LogObj("");
    description();
    // save header
    out.write((char*) &_blockHeader.container, sizeof(_containerBlockHeader));
    // save entry table
    std::vector<ksBlockEntry*>::iterator it;
    for (it = _entries.begin(); it != _entries.end(); it++)
    {
        (*it)->save(out);
    }
    // save data
    std::vector<ksBlock*>::iterator itBlock;
    for (itBlock = _blocks.begin(); itBlock != _blocks.end(); itBlock++)
    {
        (*itBlock)->writer()->save(out);
    }
}
